package com.kuaiyin.executor;

import com.fasterxml.jackson.databind.JsonNode;
import com.kuaiyin.entity.Request;
import com.kuaiyin.enums.HttpMethod;
import com.kuaiyin.exception.BaseException;
import com.kuaiyin.utils.HttpClientUtils;
import com.kuaiyin.utils.JsonFieldFinder;
import com.kuaiyin.utils.JsonVariableExtractorUtils;
import com.kuaiyin.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 抽象请求执行器，封装通用逻辑。
 * 所有具体的请求执行器都应继承此类，并实现抽象方法。
 */
@Slf4j
public abstract class AbstractRequestExecutor {

    protected final RedisUtils redisUtils;

    /**
     * 构造函数，注入 Redis 工具类
     *
     * @param redisUtils Redis 工具类实例
     */
    public AbstractRequestExecutor(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    /**
     * 模板方法：定义整个请求执行流程。
     * 子类通过重写抽象方法来自定义行为。
     *
     * @param id 请求标识符（如测试用例 ID）
                * @param planId 计划ID（可选）
     * @throws IOException IO 异常
     */
    public final void execute(Long id, Long planId) throws IOException {
        // 1. 获取基础数据
        Request request = getRequest(id);
        String environmentType = getEnvironmentType(id);
        Map<String, Object> assertions = getAssertions(id);

        // 2. 获取环境变量（从 Redis 中获取全局变量和提取变量）
        Map<String, String> envMap = getEnvironmentVariables(environmentType, planId);
        // 3. 构建最终 URL（替换路径中的 {{变量}}）
        String baseUrl = buildUrl(request.getPath(), envMap);

        // 4. 替换请求头和请求体中的变量
        Map<String, Object> headers = getRequestData(envMap, request.getHeaders());
        Map<String, Object> body = getRequestBody(request, envMap);

        // 5. 发送 HTTP 请求
        JsonNode response = sendHttpRequest(baseUrl, headers, body, request.getMethod());

        // 6. 提取响应字段并存入 Redis
        extractAndSaveResponse(id, environmentType, response);

        // 7. 将请求和响应信息保存到 Redis
        saveToRedis(id, baseUrl, body, headers, response, assertions, request.getMethod());

        // 8. 验证断言
        validateAssertions(response, assertions);

        // 9. 更新执行状态为成功
        updateExecutionStatus(id, "SUCCESS");

    }

    /**
     * 模板方法：定义整个请求执行流程（兼容旧版本，不带planId）。
     * 子类通过重写抽象方法来自定义行为。
     *
     * @param id 请求标识符（如测试用例 ID）
     * @throws IOException IO 异常
     */
    public final void execute(Long id) throws IOException {
        execute(id, null);
    }

    /**
     * 获取请求对象（子类实现）
     *
     * @param id 请求唯一标识
     * @return 请求对象
     */
    protected abstract Request getRequest(Long id);

    /**
     * 获取环境类型（dev/test/prod）（子类实现）
     *
     * @param id 请求唯一标识
     * @return 环境类型字符串
     */
    protected abstract String getEnvironmentType(Long id);

    /**
     * 获取断言信息（子类实现）
     *
     * @param id 请求唯一标识
     * @return 断言信息 Map
     */
    protected abstract Map<String, Object> getAssertions(Long id);

    /**
     * 获取环境变量（包括全局变量和提取变量）
     *
     * @param environmentType 环境类型（如 dev）
     * @param planId              请求唯一标识
     * @return 环境变量 Map
     */
    private Map<String, String> getEnvironmentVariables(String environmentType, Long planId) {
        Map<String, String> envMap = redisUtils.getHashEntriesAsStringMap("environment:" + environmentType);
        Map<String, String> extractMap = redisUtils.getHashEntriesAsStringMap("testPlan_id_" + planId + "_responseExtract_" + environmentType);
        extractMap.replaceAll((key, value) -> {  // 处理字符串中的双引号
            if (value.startsWith("\\\"") && value.endsWith("\\\"")) {
                return value.substring(2, value.length() - 2); // 去除 \\" 和 \\"
            } else if (value.startsWith("\"") && value.endsWith("\"")) {
                return value.substring(1, value.length() - 1); // 去除 " 和 "
            }
            return value;
        });
        envMap.putAll(extractMap);
        return envMap;
    }

    /**
     * 构建最终 URL，替换路径中的 {{变量名}} 为实际值
     *
     * @param path   原始路径（可能包含变量）
     * @param envMap 环境变量 Map
     * @return 替换后的 URL
     */
    protected String buildUrl(String path, Map<String, String> envMap) {
        if (path == null || !path.contains("{{")) return path;

        Set<String> variables = JsonVariableExtractorUtils.extractVariablesAndFunctions(path);
        for (String var : variables) {
            String value = envMap.get(var);
            if (value == null) {
                throw BaseException.of(0, "URL上的环境变量不存在：" + var);
            }
            path = path.replaceAll("\\{\\{" + var + "}}", value);
        }
        return path;
    }

    /**
     * 根据环境变量替换请求数据中的变量
     *
     * @param envMap 包含环境变量的映射
     * @param data   请求数据的映射，可能包含变量或函数
     * @return 替换变量后的请求数据映射
     */
    protected Map<String, Object> getRequestData(Map<String, String> envMap, Map<String, Object> data) {
        try {
            // 将请求数据转换为JSON字符串
            String jsonInput = JsonVariableExtractorUtils.objectToJsonString(data);
            // 如果JSON字符串中不包含变量或函数，则直接返回原始数据
            if (!JsonVariableExtractorUtils.containsVariablesOrFunctions(jsonInput)) {
                return data;
            }

            // 提取JSON字符串中的所有变量和函数
            Set<String> variables = JsonVariableExtractorUtils.extractVariablesAndFunctions(jsonInput);
            // 如果没有提取到任何变量或函数，则直接返回原始数据
            if (variables.isEmpty()) return data;

            // 准备一个映射用于存储变量替换值
            Map<String, Object> replacements = new HashMap<>();
            for (String var : variables) {
                replacements.put(var, envMap.get(var));
            }

            // 使用环境变量替换JSON字符串中的变量和函数
            String replacedJson = JsonVariableExtractorUtils.replaceVariablesAndFunctions(jsonInput, replacements);
            // 将替换后的JSON字符串安全地转换为映射对象并返回
            return JsonVariableExtractorUtils.toMapSafely(replacedJson);
        } catch (Exception e) {
            // 如果发生异常，记录警告日志并返回一个空映射
            log.warn("变量替换失败", e);
            return new HashMap<>();
        }
    }


    /**
     * 根据请求方法（GET/POST）获取请求体
     *
     * @param request 请求对象
     * @param envMap  环境变量 Map
     * @return 请求体 Map
     */
    protected Map<String, Object> getRequestBody(Request request, Map<String, String> envMap) {
        return HttpMethod.valueOf(request.getMethod()) == HttpMethod.GET ?
                getRequestData(envMap, request.getParams()) :
                getRequestData(envMap, request.getBody());
    }

    /**
     * 发送 HTTP 请求
     *
     * @param url     最终 URL
     * @param headers 请求头
     * @param body    请求体
     * @param method  请求方法（GET/POST/PUT）
     * @return JSON 响应节点
     * @throws IOException IO 异常
     */
    protected JsonNode sendHttpRequest(String url, Map<String, Object> headers, Map<String, Object> body, String method) throws IOException {
        return switch (method.toUpperCase()) {
            case "GET" -> HttpClientUtils.doGet(url, body, headers, null);
            case "POST" ->
                    HttpClientUtils.doPost(url, JsonVariableExtractorUtils.objectToJsonString(body), headers, null);
            case "PUT" ->
                    HttpClientUtils.doPut(url, JsonVariableExtractorUtils.objectToJsonString(body), headers, null);
            default -> throw BaseException.of(0, "不支持的请求方法");
        };
    }

    /**
     * 验证断言
     *
     * @param response   响应内容
     * @param assertions 断言 Map
     */
    protected void validateAssertions(JsonNode response, Map<String, Object> assertions) throws IOException {
        if (assertions == null) return;
        JsonFieldFinder finder = new JsonFieldFinder(response);
        assertions.forEach((key, value) -> {
            if (!String.valueOf(finder.findField(key, 0)).equals(String.valueOf(value))) {
                throw BaseException.of(0, "断言失败 期望：{" + key + ":" + value + "} 实际：{" + key + ":" + finder.findField(key, 0) + "}");
            }
        });
    }

    /**
     * 提取响应字段并存入 Redis（可选）
     *
     * @param id              请求唯一标识
     * @param environmentType 环境类型
     * @param response        响应内容
     */
    protected void extractAndSaveResponse(Long id, String environmentType, JsonNode response) {
        // 默认空实现，子类可重写
    }

    /**
     * 存入 Redis（可选）
     *
     * @param id         请求唯一标识
     * @param url        请求地址
     * @param body       请求体
     * @param headers    请求头
     * @param response   响应内容
     * @param assertions 断言信息
     * @param method
     */
    protected void saveToRedis(Long id, String url, Map<String, Object> body, Map<String, Object> headers, JsonNode response, Map<String, Object> assertions, String method) {
        // 默认空实现，子类可重写
    }

    /**
     * 更新执行状态（子类实现）
     *
     * @param id     请求唯一标识
     * @param status 新的状态值
     */
    protected abstract void updateExecutionStatus(Long id, String status);
}
